'''
版权：Copyright (c) 2019 China

创建日期：Tuesday August 6th 2019
创建者：ymq(ymq) - <<email>>

修改日期: Tuesday, 6th August 2019 5:54:02 pm
修改者: ymq(ymq) - <<email>>

说明
 1、我的订单部分接口服务
'''
import base64
import copy
import datetime
import hashlib
import json
import logging
import traceback
import uuid
from enum import Enum
from urllib import parse

import dicttoxml
import pandas as pd
import requests
import xmltodict

from server.pao_python.pao.data import (date_to_string, process_db,
                                        string_to_date)
from server.pao_python.pao.service.data.mongo_db import (C, F, MongoFilter,
                                                         MongoService, N,
                                                         as_date)

from ...service.buss_pub.bill_manage import (BillManageService, OperationType,
                                             Status, TypeId)
from ...service.common import (cal_sign, execute_python,
                               get_current_organization_id,
                               get_current_user_id, get_rank_num)
from ...service.mongo_bill_service import MongoBillFilter

# from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
# from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
# from alipay.aop.api.FileItem import FileItem
# from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel
# from alipay.aop.api.domain.AlipayTradeWapPayModel import AlipayTradeWapPayModel
# from alipay.aop.api.domain.AlipayTradePayModel import AlipayTradePayModel
# from alipay.aop.api.domain.GoodsDetail import GoodsDetail
# from alipay.aop.api.domain.SettleDetailInfo import SettleDetailInfo
# from alipay.aop.api.domain.SettleInfo import SettleInfo
# from alipay.aop.api.domain.SubMerchant import SubMerchant
# from alipay.aop.api.request.AlipayOfflineMaterialImageUploadRequest import AlipayOfflineMaterialImageUploadRequest
# from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayRequest
# from alipay.aop.api.request.AlipayTradePayRequest import AlipayTradePayRequest
# from alipay.aop.api.response.AlipayOfflineMaterialImageUploadResponse import AlipayOfflineMaterialImageUploadResponse
# from alipay.aop.api.response.AlipayTradePayResponse import AlipayTradePayResponse


class ThirdPartPayStatus(Enum):
    '''第三方支付状态枚举值'''
    payed = '已支付'
    unpay = '待支付'
    fail = '付款失败'
    refunding = '退款中'
    refunded = '已退款'
    sign_fail = '校验签名失败'


class RecordStatus(Enum):
    '''服务记录的状态枚举（服务状态为必填字段）'''
    service_completed = '已完成'
    service_going = '服务中'
    service_before = '未服务'


class OrderStatus(Enum):
    '''我的订单接口中传递condition中的状态枚举值，也是订单状态的枚举值'''
    order_unserved = '未服务'
    order_going = '服务中'
    order_completed = '已完成'
    order_assign = '已分派'


class MyOrderService(MongoService):
    def __init__(self, db_addr, db_port, db_name, db_user, db_pwd, inital_password, session):
        self.db_addr = db_addr
        self.db_port = db_port
        self.db_name = db_name
        self.db_user = db_user
        self.db_pwd = db_pwd
        self.inital_password = inital_password
        self.session = session
        self.bill_manage_server = BillManageService(
            self.db_addr, self.db_port, self.db_name, self.db_user, self.db_pwd, self.inital_password, self.session)

    def my_order_list(self, condition, page, count):
        person_id = get_current_user_id(self.session)
        keys = ['status', 'id', 'pay_state']
        values = self.get_value(condition, keys)
        _filter = MongoBillFilter()
        #    .add_fields({'order_status': self.ao.switch([self.ao.case(F('record_info') == [], OrderStatus.order_unserved.value), self.ao.case(self.ao.inner(RecordStatus.service_going.value, '$record_info.status'), OrderStatus.order_going.value)], OrderStatus.order_completed.value)})\
        _filter.match_bill((C('purchaser_id') == person_id) & (C('id') == values['id']))\
               .lookup_bill('PT_Service_Record', 'id', 'order_id', 'record_info')\
               .match(C('pay_state') == values['pay_state'])\
               .match(C('status') == values['status'])\
               .lookup_bill('PT_User', 'purchaser_id', 'id', 'purchaser_info')\
               .lookup_bill('PT_Service_Order_Comment', 'id', 'order_id', 'comment_info')\
               .lookup_bill('PT_User', 'service_provider_id', 'id', 'provider_info')\
               .lookup_bill('PT_Service_Product', 'detail.product_id', 'id', 'product_info')\
               .lookup_bill('PT_User', 'record_info.servicer_id', 'id', 'servicer_info')\
               .add_fields({'orgnization_name': '$provider_info.name', 'product_name': '$product_info.name',
                            'price_info': {'price': '$product_info.service_package_price', 'id': '$product_info.id'},
                            'phone_number': '$purchaser_info.personnel_info.telephone',
                            'product_img': self.ao.array_elemat('$product_info.picture_collection', 0),
                            'purchaser_name': '$purchaser_info.name', 'formula': '$product_info.valuation_formula'})\
               .project({'_id': 0, 'record_info._id': 0, 'option_list': 0, 'purchaser_info': 0, 'provider_info': 0, 'product_info': 0, 'servicer_info._id': 0, 'comment_info._id': 0})
        res = self.page_query(_filter, 'PT_Service_Order', page, count)
        res['result'] = self.__handle_order_data(res['result'])
        return res

    # 服务商获取服务订单
    def service_order(self, condition, page, count):
        service_id = get_current_organization_id(self.session)
        keys = ['order_status', 'id']
        values = self.get_value(condition, keys)
        _filter = MongoBillFilter()
        _filter.match_bill((C('service_provider_id') == service_id) & (C('id') == values['id']))\
               .lookup_bill('PT_Service_Record', 'id', 'order_id', 'record_info')\
               .add_fields({'order_status': self.ao.switch([self.ao.case(F('record_info') == [], OrderStatus.order_unserved.value), self.ao.case(self.ao.inner(RecordStatus.service_going.value, '$record_info.status'), OrderStatus.order_going.value)], OrderStatus.order_completed.value)})\
               .match(C('order_status') == values['order_status'])\
               .lookup_bill('PT_User', 'purchaser_id', 'id', 'purchaser_info')\
               .lookup_bill('PT_User', 'service_provider_id', 'id', 'provider_info')\
               .lookup_bill('PT_Service_Product', 'record_info.service_product_id', 'id', 'product_info')\
               .lookup_bill('PT_User', 'record_info.servicer_id', 'id', 'servicer_info')\
               .add_fields({'orgnization_name': '$provider_info.name', 'product_name': '$product_info.name',
                            'phone_number': '$purchaser_info.personnel_info.telephone',
                            'purchaser_address': '$purchaser_info.personnel_info.address',
                            'product_img': self.ao.array_elemat('$product_info.picture_collection', 0),
                            'purchaser_name': '$purchaser_info.name', 'formula': '$product_info.valuation_formula'})\
               .project({'_id': 0, 'record_info._id': 0, 'option_list': 0, 'purchaser_info': 0, 'provider_info': 0, 'product_info': 0, 'servicer_info._id': 0})
        res = self.page_query(_filter, 'PT_Service_Order', page, count)
        res['result'] = self.__handle_order_data(res['result'])
        return res

    # 服务商上传图片
    def upload_service_img(self, condition):
        if 'end_img' in condition.keys() and len(condition['end_img']) > 0:
            condition['status'] = '已完成'
        else:
            condition['status'] = '服务中'
        res = 'Fail'
        bill_id = self.bill_manage_server.add_bill(OperationType.update.value,
                                                   TypeId.user.value, condition, 'PT_Service_Order')
        if bill_id:
            res = 'Success'
        return res

    def third_pay_record_add_or_update(self, data):
        tabele_name = 'IEC_third_part_trade_record'
        result = self.bill_manage_server.add_bill(
            OperationType.update.value if 'id' in data.keys() else OperationType.add.value,
            TypeId.thirdPartPay.value,
            data,
            tabele_name
        )
        return result

    def wechat_payment_request(self, condition, realm_name, wechat_payment_data):
        '''微信支付请求'''
        def get_id_from_out_trade_no(out_trade_no):
            _filter = MongoBillFilter()
            _filter.match_bill((C('out_trade_no') == out_trade_no))\
                .project({'_id': 0})
            res = self.query(_filter, 'IEC_third_part_trade_record')
            if len(res) > 0:
                return res[0]['id']
            return False

        def third_pay_record_add_or_update(data):
            tabele_name = 'IEC_third_part_trade_record'
            result = self.bill_manage_server.add_bill(
                OperationType.update.value if 'id' in data.keys() else OperationType.add.value,
                TypeId.thirdPartPay.value,
                data,
                tabele_name
            )
            return result

        def get_third_trade_data(data):
            ''' 
                第三方交易记录表，
                表字段包括：
                    唯一业务订单号id、
                    第三方支付唯一订单号、
                    发起订单人id（付款方id）、
                    交易金额、
                    发起时间、
                    支付时间、
                    支付状态（待付款、已付款、退款中、已退款）
            '''
            return {
                "transaction_id": data['transaction_id'],
                "out_trade_no": data['out_trade_no'],
                "payer_id": data['data'],
                "total_fee": data['total_fee'],
                "trade_time": datetime.datetime.now(),
                "pay_time": None,
                "pay_status": ThirdPartPayStatus.unpay.value
            }

        time_out = 10
        # 浏览器打开的移动网页的主页title名
        page_title = condition['page_title']
        # 商品概述
        product_desc = condition['product_desc']
        # 商品订单号
        out_trade_no = condition['out_trade_no']
        # 附加信息
        attach = condition['attach'] if 'attach' in condition else None
        # 付款金额
        total_fee = condition['total_fee'] * 100
        # TODO: 客户端ip
        spbill_create_ip = "192.168.95.43"
        # 接受通知的地址
        notify_url = realm_name+wechat_payment_data['interface']
        # 商户平台设置的密钥key
        mch_key = wechat_payment_data['mch_key']
        # 商户号
        mch_id = wechat_payment_data['mch_id']
        # 统一下单接口
        unifiedorder_url = wechat_payment_data['unifiedorder_url']
        # 支付成功页面
        success_page = wechat_payment_data['success_page']
        # 公众账号id
        appid = wechat_payment_data['appid']

        # 支付成功重定向页面

        redirect_url_param = {
            'type': '微信支付',
            'amount': total_fee
        }
        redirect_url_param_deconde = parse.quote(json.dumps(
            redirect_url_param), encoding='utf-8', safe=',:/')
        redirect_url_param_deconde_b64 = base64.b64encode(
            redirect_url_param_deconde.encode()).decode()
        redirect_url = realm_name + '/app/paymentSucceed/'

        redirect = redirect_url + redirect_url_param_deconde_b64
        # 结构要求：https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_1
        json_data = {
            # 公众账号ID
            "appid": appid,
            # 商户号
            "mch_id": mch_id,
            # 随机数
            "nonce_str": get_rank_num(),
            # 商品描述
            "body": page_title+'-'+product_desc,
            # 附加信息
            "attach": attach,
            # 商户订单号
            "out_trade_no": out_trade_no,
            # 标价金额
            "total_fee": int(total_fee),
            # 用户的客户端IP
            "spbill_create_ip": '120.237.15.234',
            # 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数
            "notify_url": notify_url,
            # 交易类型
            "trade_type": "MWEB",
        }

        json_data['sign'] = cal_sign(json_data, mch_key)
        xml_to_send = dicttoxml.dicttoxml(
            json_data,  custom_root='xml', cdata=True, attr_type=False)
        # xml_to_send_str = xml_to_send.decode('utf-8')

        db_id = get_id_from_out_trade_no(out_trade_no)
        # 第三方交易表更新
        if db_id:
            self.third_pay_record_add_or_update({
                "id": db_id,
                "trade_time": datetime.datetime.now(),
            })
        else:
            self.third_pay_record_add_or_update({
                "transaction_id": None,
                "out_trade_no": out_trade_no,
                "total_fee": total_fee,
                "trade_time": datetime.datetime.now(),
                "pay_time": None,
                "pay_status": ThirdPartPayStatus.unpay.value
            })

        try:
            r = requests.post(unifiedorder_url,
                              timeout=time_out, data=xml_to_send)
            # 等待返回内容
            if r.status_code == 200:
                result_dict = xmltodict.parse(r.text)['xml']
                # 返回状态码，此字段是通信标识，非交易标识，交易是否成功需要查看result_code来判断
                return_code = result_dict['return_code']
                # 返回信息，当return_code为FAIL时返回信息为错误原因 ，例如签名失败、参数格式校验错误
                # result_msg = result_dict['return_msg'].encode(
                #     r.encoding).decode('utf-8')
                if return_code == 'SUCCESS':
                    # 业务结果
                    result_code = result_dict['result_code']
                    # 业务处理失败
                    if result_code == 'FAIL':
                        err_code = result_dict['err_code']
                        err_code_des = result_dict['err_code_des'].encode(
                            r.encoding).decode('utf-8')
                        return {
                            "result_code": result_code,
                            "err_code": err_code,
                            "err_code_des": err_code_des
                        }
                    # 业务处理成功
                    else:
                        return {
                            # 返回跳转url
                            'mweb_url': result_dict['mweb_url']
                        }
                else:
                    pass
            else:
                r.raise_for_status()  # 抛出异常
        except requests.exceptions.Timeout as e:
            return [{
                "result": "超时异常："+str(e)
            }]

    def third_trade_query(self, condition):
        '''第三方交易记录查询'''
        person_id = get_current_user_id(self.session)
        keys = ['id', 'out_trade_no']
        values = self.get_value(condition, keys)
        _filter = MongoBillFilter()
        _filter.match_bill((C('id') == values['id']) & (C('out_trade_no') == values['out_trade_no']))\
               .project({'_id': 0})
        res = self.query(_filter, 'IEC_third_part_trade_record')

        return res

    def get_user_id(self):
        '''第三方交易记录查询'''
        person_id = get_current_user_id(self.session)

        return person_id

    # 支付宝请求
    def alipay_request(self, condition, realm_name, alipay_payment_data):
        """
        官方文档：https://docs.open.alipay.com/203
        页面接口示例：alipay.trade.wap.pay
        """
        # 支付宝分配给开发者的应用ID
        app_id = alipay_payment_data['app_id']
        pu_k = alipay_payment_data['pu_k']
        pr_k = alipay_payment_data['pr_k']
        # 商户网站唯一订单号
        out_trade_no = "pay201805020000226"
        # 订单总金额，单位为元，精确到小数点后两位，取值范围[0.01,100000000]
        total_amount = 1
        # 商品的标题/交易标题/订单标题/订单关键字等
        subject = "测试"
        # 用户付款中途退出返回商户网站的地址
        quit_url = ""
        # 销售产品码，商家和支付宝签约的产品码
        product_code = ""
        # 请求使用的编码格式，如utf-8,gbk,gb2312等
        charset = "utf-8"
        # 商户生成签名字符串所使用的签名算法类型，目前支持RSA2和RSA，推荐使用RSA2
        sign_type = ""
        # 商户请求参数的签名串，详见https://docs.open.alipay.com/291/105974
        sign = ""
        # 发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
        timestamp = ""
        # 请求参数的集合，最大长度不限，除公共参数外所有请求参数都必须放在这个参数中传递，具体参照各产品快速接入文档
        biz_content = ""
        body = ""
        notify_url = ""

#     logging.basicConfig(
#         level=logging.INFO,
#         format='%(asctime)s %(levelname)s %(message)s',
#         filemode='a',)
#     logger = logging.getLogger('')
#     """
#     设置配置，包括支付宝网关地址、app_id、应用私钥、支付宝公钥等，其他配置值可以查看AlipayClientConfig的定义。
#     """
#     alipay_client_config = AlipayClientConfig()
#     alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
#     alipay_client_config.app_id = app_id
#     alipay_client_config.app_private_key = pr_k
#     alipay_client_config.alipay_public_key = pu_k
#     alipay_client_config.sign_type = "RSA"

#     """
#     得到客户端对象。
#     注意，一个alipay_client_config对象对应一个DefaultAlipayClient，定义DefaultAlipayClient对象后，alipay_client_config不得修改，如果想使用不同的配置，请定义不同的DefaultAlipayClient。
#     logger参数用于打印日志，不传则不打印，建议传递。
#     """
#     client = DefaultAlipayClient(
#         alipay_client_config=alipay_client_config, logger=logger)

#     """
#     页面接口示例：alipay.trade.page.pay
#     """
#     # 对照接口文档，构造请求对象
#     model = AlipayTradeWapPayModel()
#     # TODO: 唯一订单号
#     model.out_trade_no = "pay201805020000226"
#     model.total_amount = 50
#     # TODO: 商品的标题/交易标题/订单标题/订单关键字等。
#     model.subject = "测试"
#     # TODO: 对一笔交易的具体描述信息。如果是多种商品，请将商品描述字符串累加传给body。
#     model.body = "支付宝测试"
#     model.product_code = "QUICK_WAP_WAY"
#     request = AlipayTradeWapPayRequest(biz_model=model)
#     # 得到构造的请求，如果http_method是GET，则是一个带完成请求参数的url，如果http_method是POST，则是一段HTML表单片段
#     try:
#         response = client.page_execute(request, http_method="GET")
#     except Exception as e:
#         print(traceback.format_exc())
#     print("alipay.trade.wap.pay response:" + response)

# 支付宝支付异步响应回调
# class AliPayNotifyUrlController(tornado.web.RequestHandler):
#     """
#     支付宝手机网站支付结果异步通知
#     官方文档：https://docs.open.alipay.com/203/105286/
#     """

#     def __init__(self, application, request, **kwargs):
#         super(AliPayNotifyUrlController, self).__init__(application, request, **kwargs)
#         self.title = "支付宝支付"

#     def post(self):
#         notify_time = self.get_argument("notify_time")
#         notify_type = self.get_argument("notify_type", "")
#         notify_id = self.get_argument("notify_id", "")
#         subject = self.get_argument("subject")
#         body = self.get_argument("body")
#         app_id = self.get_argument("app_id")
#         seller_id = self.get_argument("seller_id")
#         seller_email = self.get_argument("seller_email")
#         buyer_id = self.get_argument("buyer_id")
#         auth_app_id = self.get_argument("auth_app_id")
#         buyer_logon_id = self.get_argument("buyer_logon_id")
#         fund_bill_list = self.get_argument("fund_bill_list")
#         fund_bill_list = json.loads(fund_bill_list)
#         fund_bill_list_account = fund_bill_list[0]["amount"]
#         fund_bill_list_fundChannel = fund_bill_list[0]["fundChannel"]
#         trade_status = self.get_argument("trade_status")
#         total_amount = self.get_argument("total_amount")
#         invoice_amount = self.get_argument("invoice_amount")
#         receipt_amount = self.get_argument("receipt_amount")
#         buyer_pay_amount = self.get_argument("buyer_pay_amount")
#         point_amount = self.get_argument("point_amount")
#         version = self.get_argument("version")
#         out_trade_no = self.get_argument("out_trade_no")
#         trade_no = self.get_argument("trade_no")
#         charset = self.get_argument("charset")
#         sign = self.get_argument("sign")
#         sign_type = self.get_argument("sign_type")
#         gmt_create = self.get_argument("gmt_create")
#         gmt_payment = self.get_argument("gmt_payment", None)
#         gmt_close = self.get_argument("gmt_close", None)

#         model = MyAliPayOrderModel()
#         model.notify_time = notify_time
#         model.notify_type = notify_type
#         model.notify_id = notify_id
#         model.subject = subject
#         model.body = body
#         model.app_id = app_id
#         model.seller_id = seller_id
#         model.seller_email = seller_email
#         model.buyer_id = buyer_id
#         model.auth_app_id = auth_app_id
#         model.buyer_logon_id = buyer_logon_id
#         model.fund_bill_list = fund_bill_list
#         model.fund_bill_list_account = fund_bill_list_account
#         model.fund_bill_list_fundChannel = fund_bill_list_fundChannel
#         model.trade_status = trade_status
#         model.total_amount = total_amount
#         model.invoice_amount = invoice_amount
#         model.receipt_amount = receipt_amount
#         model.buyer_pay_amount = buyer_pay_amount
#         model.point_amount = point_amount
#         model.version = version
#         model.out_trade_no = out_trade_no
#         model.trade_no = trade_no
#         model.charset = charset
#         model.sign = sign
#         model.sign_type = sign_type
#         model.gmt_create = gmt_create
#         model.gmt_payment = gmt_payment
#         model.gmt_close = gmt_close

#         # 更新到数据库
#         # to do something

#         # 如果商户反馈给支付宝的字符不是success这7个字符，支付宝服务器会不断重发通知，直到超过24小时22分钟。
#         # 一般情况下，25小时以内完成8次通知（通知的间隔频率一般是：4m,10m,10m,1h,2h,6h,15h）。
#         self.write("success")
#         self.finish()

    def __handle_order_data(self, data):
        '''计算每个服务产品的总价'''
        for i in data:
            i['product_amount'] = []
            if i.get("detail"):
                for product in i['detail']:
                    if product.get('valuation_formula'):
                        if product.get('valuation_formula')[0].get('formula'):
                            valuation_formula = product.get('valuation_formula')[
                                0].get('formula')
                            if product.get('service_option'):
                                options = product.get('service_option')
                                for option in options:
                                    if option.get('option_value'):
                                        amount = execute_python(
                                            valuation_formula, [option], 'name', 'option_value')
                                        i['product_amount'].append(amount)
                                        i['amount'] = sum(
                                            i['product_amount']) * float(product.get('count'))
        return data
